package net.lesscoding.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReUtil;
import lombok.extern.slf4j.Slf4j;
import net.lesscoding.common.ColumnInfo;
import net.lesscoding.common.CommonConst;
import net.lesscoding.common.TableInfo;
import net.lesscoding.config.GenerateConfig;
import net.lesscoding.config.RegExrConfig;
import net.lesscoding.config.TemplateConfig;
import net.lesscoding.entity.OutPathConfig;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author eleven
 * @date 2022/7/13 15:33
 * @description
 */
@Slf4j
public class GeneratorUtil {
    /**
     * 通过模板文件的路径获取template对象
     * @param   path        模板文件的路径
     * @return  Template    返回创建好的模板对象
     */
    public static Template buildTemplate(String path){
        Properties properties = new Properties();
        properties.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        properties.put("directive.foreach.counter.name", "velocityCount");
        properties.put("directive.foreach.counter.initial.value", "1");
        Velocity.init(properties);
        Template template = Velocity.getTemplate(path,"utf-8");
        return template;
    }

    /**
     * 获取共用context配置
     * @param templateConfig    用户选择的模板配置
     * @return Map
     */
    public static Map<String,Object> commonMap(TemplateConfig templateConfig){
        GenerateConfig generateConfig = templateConfig.getGenerateConfig();
        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("basePackage",generateConfig.getBasePackage());
        resultMap.put("author",generateConfig.getAuthor());
        resultMap.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        resultMap.put("userChoose",templateConfig.getUserChoose());
        resultMap.put("message","$message");
        resultMap.put("ref","$ref");
        resultMap.put("router","$router");
        return  resultMap;
    }

    /**
     * 将输入流按照路径写出到压缩包输出流中
     * @param zos           压缩包输出流
     * @param inputStream   生成文件的输入流
     * @param path          要写入压缩包内的路径
     */
    public static void writeToZipOutStream(ZipOutputStream zos, InputStream inputStream, String path){
        try {
            zos.putNextEntry(new ZipEntry(path));
            int len;
            // 每次写出8KB
            byte[] data = new byte[1 << 13];
            while ((len = inputStream.read(data)) > 0) {
                zos.write(data, 0, len);
            }
            zos.closeEntry();
            inputStream.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }


    /**
     * 获取所有的字段
     * @param sql   sql语句
     * @return
     */
    public static List<String> getAllColumn(String sql){

        // 按照 , 分割开获取字段集合
        List<String> colList = Arrays.asList(sql.trim().split(CommonConst.COMMA));
        LinkedList<String> columnStrList = new LinkedList<>();
        columnStrList.addAll(colList);
        List<Integer> errorList = new ArrayList<>();
        /**
         *  如果当前行以数字结尾，并且下一行以数字开头，则放置到错误集合中
         *  因为按照了 , 进行分割，如果出现double(5,2)这种就会变成行数据
         */
        for (int i = 1; i < columnStrList.size(); i++) {
            String prev = columnStrList.get(i - 1).trim();
            String next = columnStrList.get(i).trim();
            if(isNumberEndOrStart(prev,false) && isNumberEndOrStart(next,true)){
                errorList.add(i);
            }
        }
        /**
         * 将double这种错误分割的数据重新拼接成一行，然后将以数字开头的数据删除掉
         */
        for (int index : errorList) {
            String next = columnStrList.get(index).trim();
            String prev = columnStrList.get(index - 1).trim();
            prev = prev + ',' + next;
            columnStrList.set(index - 1,prev);
            columnStrList.remove(index);
        }
        return columnStrList;
    }

    /**
     * 判断一个字符串是不是以数字结尾或者以数字开头
     * @param str           要判断的字符串
     * @param startType     true 判断开头字符 false 判断结尾字符
     * @return Boolean
     */
    public static Boolean isNumberEndOrStart(String str,Boolean startType){
        char validChar = startType ? str.charAt(0) : str.charAt(str.length() - 1);
        return validChar >= '0' && validChar <= '9';
    }

    /**
     * 生成输出路径
     * @param outPath           输出路径
     * @param pathName          顶级目录名称
     * @param context           context模板
     * @param useBasePackage    是否使用basePackage
     * @param ui                是否ui文件夹
     * @return
     */
    private static String getPath(String outPath, String pathName, VelocityContext context, Boolean useBasePackage,Boolean ui) {
        pathName += ui ? "_UI" : "";
        TableInfo tableInfo = (TableInfo) context.get("tableInfo");
        String basePackage = context.get("basePackage").toString();
        return useBasePackage ?
                String.format(outPath,pathName,packageToPath(basePackage),tableInfo.getClassName()):
                String.format(outPath,pathName,tableInfo.getClassName());
    }

    /**
     * 将basePackage转换成 目录的形式
     * 由com.xxx转换成com/xxx
     * @param basePackage   基础目录  com.xxx
     * @return
     */
    private static String packageToPath(String basePackage){
        return basePackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator));
    }

    /**
     * 将输入流变更为字符串
     * @param is  用户上传的sql文件的输入流
     * @return String 将sql文件转换成Stirng字符串
     */
    public static String inputStreamToString(InputStream is){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            byte[] data = new byte[1 << 20];
            int len;
            while ((len = is.read(data)) != -1) {
                baos.write(data,0,len);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
        return baos.toString().toLowerCase();
    }

    /**
     * 生成输出路径
     * @param outPathConfig 输出配置信息
     * @param pathName      路径名称
     * @param context       context模板
     * @return
     */
    public static String getPath(OutPathConfig outPathConfig, String pathName, VelocityContext context) {
        return getPath(outPathConfig.getOutPath(),pathName,context,outPathConfig.getUseBasePackage(),outPathConfig.getUiFlag());
    }
}
